{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 背包问题"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 概览\n",
    "- 非离散的情况（比如偷半袋米...）不可以用DP，而贪心可以。且使用DP时子问题不能有相互依赖，即独立。\n",
    "- 书上介绍的问题实际上是0-1背包问题，即对商品要么拿一个，要么不拿\n",
    "- 解决方法实为“填二维表”的方法，此外还有“填一维表”的方法。\n",
    "- 代码实现的时候多了一行和一列作为初始化的需要。\n",
    "- 时间复杂度和空间复杂度均为$O(nC)$, 其中$n$为物品总数，$C$为背包承重"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 代码实现\n",
    "\n",
    "参考[0-1背包问题的动态规划算法](https://zhuanlan.zhihu.com/p/30959069), [Knapsack-problem-0-1-Dynamic-programming-solution](https://rosettacode.org/wiki/Knapsack_problem/0-1#Dynamic_programming_solution)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-12-30T02:51:40.697417Z",
     "start_time": "2018-12-30T02:51:40.679037Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bagged the following items\n",
      "  IPhone\n",
      "  Laptop\n",
      "for a total value of 4000 and a total weight of 4\n"
     ]
    }
   ],
   "source": [
    "items = (('Guitar', 1, 1500), ('Speaker', 4, 3000), \n",
    "         ('Laptop', 3, 2000), ('IPhone', 1, 2000)\n",
    "        )\n",
    "\n",
    "def knapsack01_dp(items, limit):\n",
    "    # 初始化二维表\n",
    "    table = [[0 for i in range(limit+1)] for j in range(len(items)+1)]\n",
    "    # 开始填表\n",
    "    for j in range(1, len(items)+1):\n",
    "        item, wt, val = items[j-1]\n",
    "        for w in range(1, limit+1):\n",
    "            if wt > w:\n",
    "                table[j][w] = table[j-1][w]\n",
    "            else:\n",
    "                table[j][w] = max(table[j-1][w], \n",
    "                                  table[j-1][w-wt]+val)\n",
    "    result = []\n",
    "    w = limit\n",
    "    for j in range(len(items), 0, -1):\n",
    "        was_added = table[j][w] != table[j-1][w]\n",
    "        if was_added:\n",
    "            item, wt, val = items[j-1]\n",
    "            result.append(items[j-1])\n",
    "            w -= wt\n",
    "    return result\n",
    "\n",
    "def totalvalue(comb):\n",
    "    ' Totalise a particular combination of items'\n",
    "    totwt = totval = 0\n",
    "    for item, wt, val in comb:\n",
    "        totwt  += wt\n",
    "        totval += val\n",
    "    return (totval, -totwt) if totwt <= 400 else (0, 0)\n",
    "\n",
    "bagged = knapsack01_dp(items, 4)\n",
    "print(\"Bagged the following items\\n  \" +\n",
    "      '\\n  '.join(sorted(item for item,_,_ in bagged)))\n",
    "val, wt = totalvalue(bagged)\n",
    "print(\"for a total value of %i and a total weight of %i\" % (val, -wt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习9.2\n",
    "\n",
    "直接把上面的items改成给所给的数据即可\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-12-30T12:52:42.321906Z",
     "start_time": "2018-12-30T12:52:42.299873Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bagged the following items\n",
      "  水\n",
      "  相机\n",
      "  食物\n",
      "for a total value of 25 and a total weight of 6\n"
     ]
    }
   ],
   "source": [
    "items = (('水', 3, 10), ('书', 1, 3), \n",
    "         ('食物', 2, 9), ('夹克', 2, 5),\n",
    "         ('相机', 1, 6)\n",
    "        )\n",
    "\n",
    "def knapsack01_dp(items, limit):\n",
    "    # 初始化二维表\n",
    "    table = [[0 for i in range(limit+1)] for j in range(len(items)+1)]\n",
    "    # 开始填表\n",
    "    for j in range(1, len(items)+1):\n",
    "        item, wt, val = items[j-1]\n",
    "        for w in range(1, limit+1):\n",
    "            if wt > w:\n",
    "                table[j][w] = table[j-1][w]\n",
    "            else:\n",
    "                table[j][w] = max(table[j-1][w], \n",
    "                                  table[j-1][w-wt]+val)\n",
    "    result = []\n",
    "    w = limit\n",
    "    for j in range(len(items), 0, -1):\n",
    "        was_added = table[j][w] != table[j-1][w]\n",
    "        if was_added:\n",
    "            item, wt, val = items[j-1]\n",
    "            result.append(items[j-1])\n",
    "            w -= wt\n",
    "    return result\n",
    "\n",
    "def totalvalue(comb):\n",
    "    totwt = totval = 0\n",
    "    for item, wt, val in comb:\n",
    "        totwt  += wt\n",
    "        totval += val\n",
    "    return (totval, -totwt) if totwt <= 400 else (0, 0)\n",
    "\n",
    "bagged = knapsack01_dp(items, 6)\n",
    "print(\"Bagged the following items\\n  \" +\n",
    "      '\\n  '.join(sorted(item for item,_,_ in bagged)))\n",
    "val, wt = totalvalue(bagged)\n",
    "print(\"for a total value of %i and a total weight of %i\" % (val, -wt))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  },
  "toc": {
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
